package com.ljh.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ljh.api.pojo.UserInfoRegisterRequest;
import com.ljh.api.pojo.UserInfoRequest;
import com.ljh.api.pojo.UserInfoResponse;
import com.ljh.api.pojo.UserLoginResponse;
import com.ljh.blog.api.BlogServiceApi;
import com.ljh.blog.api.pojo.BlogInfoResponse;
import com.ljh.common.constants.Constants;
import com.ljh.common.pojo.Result;
import com.ljh.common.utils.*;
import com.ljh.user.convert.BeanConvert;
import com.ljh.user.dataobject.UserInfo;
import com.ljh.user.mapper.UserInfoMapper;
import com.ljh.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ljh.common.exception.BlogException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    private static final String USER_PREFIX = "user:";

    private static final Long TIMEOUT = (long) (14 * 24 * 60 * 60);

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private BlogServiceApi blogServiceApi;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public UserLoginResponse login(UserInfoRequest user) {
        // TODO 1. 验证账号密码是否正确
        UserInfo userInfo = queryUserInfo(user.getUserName());
        if (userInfo == null || userInfo.getId() == null){
            throw new BlogException("用户不存在");
        }

        if (!SecurityUtil.verify(user.getPassword(),userInfo.getPassword())){
            throw new BlogException("用户密码不正确");
        }

        // TODO 2. 账号密码正确的逻辑
        Map<String,Object> claims = new HashMap<>();
        claims.put("id", userInfo.getId());
        claims.put("name", userInfo.getUserName());

        String jwt = JWTUtils.genJwt(claims);
        return new UserLoginResponse(userInfo.getId(), jwt);
    }

    @Override
    public UserInfoResponse getUserInfo(Integer userId) {
        UserInfoResponse userInfoResponse = new UserInfoResponse();
        UserInfo userInfo = selectUserInfoById(userId);
        BeanUtils.copyProperties(userInfo, userInfoResponse);
        return userInfoResponse;
    }

    @Override
    public UserInfoResponse selectAuthorInfoByBlogId(Integer blogId) {
        UserInfoResponse userInfoResponse = new UserInfoResponse();

        // TODO 1. 根据博客ID, 获取作者ID（远程调用）
        Result<BlogInfoResponse> blogDetail = blogServiceApi.getBlogDetail(blogId);

        // TODO 2. 根据作者ID, 获取作者信息
        if (blogDetail == null || null == blogDetail.getData()){
            throw new BlogException("博客不存在");
        }

        UserInfo userInfo = selectUserInfoById(blogDetail.getData().getUserId());
        BeanUtils.copyProperties(userInfo, userInfoResponse);
        return userInfoResponse;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer register(UserInfoRegisterRequest param) {
        // TODO 1. 校验注册参数
        checkUserInfo(param);

        // TODO 2. 存入数据库
        UserInfo userInfo = BeanConvert.convertToUserInfo(param);
        try {
            int result = userInfoMapper.insert(userInfo);
            if (result == 1) {
                // TODO 2.1 存入 redis
                redisUtil.set(USER_PREFIX + userInfo.getUserName(),
                        JSONUtil.stringfy(userInfo),
                        TIMEOUT);

                // TODO 2.2 发送邮件，通过消息队列异步发送
                userInfo.setPassword(""); // ! 密码设置为空
                rabbitTemplate.convertAndSend(Constants.USER_EXCHANGE_NAME,
                        "",
                        Objects.requireNonNull(JSONUtil.stringfy(userInfo)));

                return userInfo.getId();
            } else {
                throw new BlogException("用户注册失败");
            }
        } catch (Exception e) {
            log.info("用户注册失败：", e);
            throw new BlogException("用户注册失败");
        }
    }

    private UserInfo queryUserInfo(String userName) {
        // TODO 1. 校验参数
        if (!StringUtils.hasLength(userName)) {
            return null;
        }

        // TODO 2. 从 Redis 中获取用户信息
        String key = USER_PREFIX + userName;
        Boolean isExists = redisUtil.exists(key);
        if (isExists) {
            log.info("从 Redis 中获取用户信息...");
            String jsonUserInfo = redisUtil.get(key);
            return null == jsonUserInfo
                    ? selectUserInfoByName(userName)
                    : JSONUtil.parse(jsonUserInfo, UserInfo.class);
        } else {
            // TODO 3. 从数据库中获取用户信息
            log.info("从数据库中获取用户信息...");
            UserInfo userInfo = selectUserInfoByName(userName);

            if (null != userInfo) {
                // TODO 3.1 存入 Redis
                redisUtil.set(key, JSONUtil.stringfy(userInfo), TIMEOUT);
            }

            return userInfo;
        }
    }

    private void checkUserInfo(UserInfoRegisterRequest param) {
        // TODO 1. 用户名不能重复
        UserInfo userInfo = selectUserInfoByName(param.getUserName());
        if (null != userInfo) {
            throw new BlogException("用户名已经注册");
        }

        // TODO 2. 校验邮箱格式
        if (!RegexUtil.checkMail(param.getEmail())) {
            throw new BlogException("邮箱格式错误");
        }

        // TODO 3. 校验 url格式
        if (!RegexUtil.checkURL(param.getGithubUrl())) {
            throw new BlogException("url 格式错误");
        }
    }

    public UserInfo selectUserInfoByName(String userName) {
        return userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getUserName, userName)
                .eq(UserInfo::getDeleteFlag, 0));
    }

    private UserInfo selectUserInfoById(Integer userId) {
        return userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getId, userId)
                .eq(UserInfo::getDeleteFlag, 0));
    }
}
